﻿/* Pierwszy program Direct X w C# */


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
/* W tym miejscu dołączamy biblioteki */
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Collections.Generic;



/* Było narysowane, ale praktycznie w 2D. No to spróbujmy w 3D coś więcej */

namespace DirectX_Sample
{
    public class MyObject
    {
        private Mesh m;                 //Mesh - klasa siatki obiektu
        private Device dev;
        CustomVertex.PositionNormalColored[] verts;
        short[] indisA;
        short[] indisB;
      //  private ExtendedMaterial[] ExtendedMaterials;   // Tablica ExtendedMateriałów obiektu, z której wydobędziemy
       // private Texture[] Textures;                     // tekstury
       // private Material[] Materials;                   // i materiały
        public Vector3 center, BoxMin, BoxMax;          // center, radius - sfera otaczająca
        public float radius;                            // BoxMin,BoxMax - box otaczający

        public MyObject(ref Device d)             // prosty konstruktor
        {
            m=null;
            dev = d;
        }

        public void Render()                // Rendering obiektu na device
        {
            //dev.VertexFormat = m.VertexFormat;   //Ustalamy Format Vertexów
            ////for (int i = 0; i < ExtendedMaterials.Length; i++)      //EM.Length zawiera liczbę submeshów danego mesha
            ////{
            ////    dev.Material = Materials[i];            //każdy submesh ma inny materiał
            ////    dev.SetTexture(0, Textures[i]);         //i inną teksturę
            ////    m.DrawSubset(i);                        //rendering i-tego submesha
            ////}
            //m.DrawSubset(0);

            dev.VertexFormat = CustomVertex.PositionNormalColored.Format;  //informujemy device jaki jest format wierzchołka
            dev.DrawUserPrimitives(PrimitiveType.PointList, verts.Length, verts);  //i rysujemy trójkąt

            dev.DrawIndexedUserPrimitives(PrimitiveType.TriangleList, 0, indisA.Length, indisA.Length / 3, indisA, true, verts);
            dev.DrawIndexedUserPrimitives(PrimitiveType.TriangleList, 0, indisB.Length, indisB.Length / 3, indisB, true, verts);

        }

        private Vector3 ComputeNormal(Vector3 v0, Vector3 v1, Vector3 v2)
        {
            Vector3 a = v1 - v0;
            Vector3 b = v2 - v0;

            Vector3 result = new Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
            //shift back to original coordinates
           // result += v0;

            //scale to make it unit vector
            result.Normalize();


            return result;
        }
        public void GenerateSphere(float r, int horizontalSteps, int verticalSteps)
        {
            verts = new CustomVertex.PositionNormalColored[verticalSteps * horizontalSteps];

            float hStep = 2.0f * (float)Math.PI / horizontalSteps;
            float vStep = 1.0f * (float)Math.PI / verticalSteps;
            int k = 0;
            for (int i = 0; i < verticalSteps; i++)
            {
                float fi = i * vStep;
                for (int j = 0; j < horizontalSteps; j++)
                {
                    float teta = j * hStep;
                    float x = r *(float)( Math.Cos(teta) * Math.Sin(fi));
                    float z = r *(float) (Math.Sin(teta) * Math.Sin(fi));
                    float y = r * (float)(Math.Cos(fi));
                    Vector3 normal = new Vector3(x, y, z);
                    normal.Normalize();

                    verts[k] = new CustomVertex.PositionNormalColored(new Vector3(x, y, z), normal, Color.Yellow.ToArgb());
                    k++;
                    //Console.Out.WriteLine("({0}, {1}, {2})", x, y, z);
                }
            }

            BoxMax = new Vector3(r/2, r/2, r/2);
            BoxMin = new Vector3(-r/2, -r/2, -r/2);

            radius = r;

            indisA = new short[horizontalSteps * (verticalSteps - 1) * 3];
            indisB = new short[horizontalSteps * (verticalSteps - 1) * 3];
            for (int i = 0; i < indisA.Length / 3; i++)
            {
                int index = i;


                indisA[3 * i + 0] = (short)(index + 1);
                indisA[3 * i + 1] = (short)index;
                indisA[3 * i + 2] = (short)(index + horizontalSteps);

                if ((i + 1) % horizontalSteps == 0)
                {
                    Console.Out.WriteLine("Changin");
                    indisA[3 * i + 0] = (short)(index + 1 - horizontalSteps);
                    indisA[3 * i + 1] = (short)index;
                    indisA[3 * i + 2] = (short)(index + horizontalSteps);

                }


                indisB[3 * i + 0] = (short)(index + 1);
                indisB[3 * i + 1] = (short)(index + horizontalSteps);
                indisB[3 * i + 2] = (short)(index + horizontalSteps + 1);

                if ((i + 1) % horizontalSteps == 0)
                {
                    indisB[3 * i + 0] = (short)(index + 1 - horizontalSteps);
                    indisB[3 * i + 1] = (short)(index + horizontalSteps);
                    indisB[3 * i + 2] = (short)(index + 1);
                }
            }

        }



        public void GenerateQube(float length, float height, float depth)
        {
            CustomVertex.PositionNormalColored[] vertices = new CustomVertex.PositionNormalColored[36];

            //calculate positions
            Vector3[] positions = new Vector3[8];
            positions[0] = new Vector3(0,      0,      0);
            positions[1] = new Vector3(length, 0,      0);
            positions[2] = new Vector3(length, height, 0);
            positions[3] = new Vector3(0,      height, 0);
            positions[4] = new Vector3(0,      0,      depth);
            positions[5] = new Vector3(length, 0,      depth);
            positions[6] = new Vector3(length, height, depth);
            positions[7] = new Vector3(0,      height, depth);

            //calculate vertices
            //triangle 0
            vertices[0].Position = positions[0];
            vertices[1].Position = positions[3];
            vertices[2].Position = positions[1];

            vertices[0].Normal = vertices[1].Normal = vertices[2].Normal =
                ComputeNormal(positions[0], positions[3], positions[1]);

            //triangle 1
            vertices[3].Position = positions[1];
            vertices[4].Position = positions[3];
            vertices[5].Position = positions[2];

            vertices[3].Normal = vertices[4].Normal = vertices[5].Normal = 
                ComputeNormal(positions[1], positions[3], positions[2]);
            
            //triangle 2
            vertices[6].Position = positions[1];
            vertices[7].Position = positions[2];
            vertices[8].Position = positions[5];

            vertices[6].Normal = vertices[7].Normal = vertices[8].Normal =
                ComputeNormal(positions[1], positions[2], positions[5]);

            //triangle 3
            vertices[9].Position = positions[5];
            vertices[10].Position = positions[2];
            vertices[11].Position = positions[6];

            vertices[9].Normal = vertices[10].Normal = vertices[11].Normal =
                ComputeNormal(positions[5], positions[2], positions[6]);

            //triangle 4
            vertices[12].Position = positions[3];
            vertices[13].Position = positions[7];
            vertices[14].Position = positions[2];

            vertices[12].Normal = vertices[13].Normal = vertices[14].Normal =
                ComputeNormal(positions[3], positions[7], positions[2]);

            //triangle 5
            vertices[15].Position = positions[2];
            vertices[16].Position = positions[7];
            vertices[17].Position = positions[6];

            vertices[15].Normal = vertices[16].Normal = vertices[17].Normal =
                ComputeNormal(positions[3], positions[7], positions[2]);

            //triangle 6
            vertices[18].Position = positions[4];
            vertices[19].Position = positions[7];
            vertices[20].Position = positions[0];

            vertices[18].Normal = vertices[19].Normal = vertices[20].Normal =
                ComputeNormal(positions[4], positions[7], positions[0]);

            //triangle 7
            vertices[21].Position = positions[0];
            vertices[22].Position = positions[7];
            vertices[23].Position = positions[3];

            vertices[21].Normal = vertices[22].Normal = vertices[23].Normal =
                ComputeNormal(positions[0], positions[7], positions[3]);

            //triangle 8
            vertices[24].Position = positions[0];
            vertices[25].Position = positions[1];
            vertices[26].Position = positions[4];

            vertices[24].Normal = vertices[25].Normal = vertices[26].Normal =
                ComputeNormal(positions[0], positions[1], positions[4]);

            //triangle 9
            vertices[27].Position = positions[1];
            vertices[28].Position = positions[5];
            vertices[29].Position = positions[4];

            vertices[27].Normal = vertices[28].Normal = vertices[29].Normal =
                ComputeNormal(positions[1], positions[5], positions[4]);

            //triangle 10
            vertices[30].Position = positions[4];
            vertices[31].Position = positions[5];
            vertices[32].Position = positions[7];

            vertices[30].Normal = vertices[31].Normal = vertices[32].Normal =
                ComputeNormal(positions[4], positions[5], positions[7]);

            //triangle 11
            vertices[33].Position = positions[5];
            vertices[34].Position = positions[6];
            vertices[35].Position = positions[7];

            vertices[33].Normal = vertices[34].Normal = vertices[35].Normal =
                ComputeNormal(positions[5], positions[6], positions[7]);

            Color[] colorTab = { Color.Yellow, Color.Red, Color.Green, Color.Purple, Color.Blue, Color.Brown };
            int colorIndex = 0;
            for (int i = 0; i < vertices.Length; i++)
            {
                vertices[i].Color = colorTab[colorIndex].ToArgb();
                Console.Out.WriteLine("{0} : X = {1}, Y = {2}, Z = {3}", i, vertices[i].Normal.X, vertices[i].Normal.Y, vertices[i].Normal.Z);

                if ((i + 1) % 6 == 0) colorIndex++;
            }

            short[] indices = new short[36];
            for (short i = 0; i < indices.Length; i++)
            {
                indices[i] = i;
            }

            

            m = new Mesh(12, 36, MeshFlags.Managed, CustomVertex.PositionNormalColored.Format, dev);

            m.SetVertexBufferData(vertices, LockFlags.None);
            m.SetIndexBufferData(indices, LockFlags.None);

            {
                VertexBuffer verts = m.VertexBuffer;  // za pomocą klasy Geometry liczymy Bounding Box i Bounding Sphere
                GraphicsStream stream = verts.Lock(0, 0, LockFlags.None);
                radius = Geometry.ComputeBoundingSphere(stream, m.NumberVertices, m.VertexFormat, out center);
                Geometry.ComputeBoundingBox(stream, m.NumberVertices, m.VertexFormat, out BoxMin, out BoxMax);
                verts.Unlock();
            }


            //m.ComputeNormals();

        }
    }
    
            
    
    public class WinForm : System.Windows.Forms.Form        // Tworzymy klasę WinForm pochodną standardowej formy z WinAPI
    {
        private Device device;  //tu pierwsza nowość. Zmienna typu Device.

        private MyObject theXFile;
        private System.ComponentModel.Container components = null;
        private float t = 0;

        public WinForm()  // konstruktor - inicjalizujemy komponenty formy (tak naprawdę to ich nie ma, ale wygodniej się pracuje w VS)
        {
            InitializeComponent();
            
        }

        
        public void InitializeCamera() // To jest nowe w tej wersji. Tworzymy kamerę.
        {
            float aspect_ratio = this.Width / (float)this.Height;

            float q = 2.0f + (float)Math.Sin(4 * t); //q - pomocnicza zmienna sterująca ruchem kamery
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, aspect_ratio, 1f, theXFile.radius * 12);  // macierz projekcji
            device.Transform.View = Matrix.LookAtLH(
                theXFile.center - (new Vector3(3 * theXFile.radius * (float)Math.Cos(t), 3 * theXFile.radius * (float)Math.Sin(t), (float)3 * theXFile.radius * (float)Math.Sin(t))),
                theXFile.center,
                new Vector3(0, 1, 0)); // macierz widoku

            device.Transform.World = Matrix.Identity;

            device.Lights[0].Type = LightType.Directional;  // tworzymy dwa światła kierunkowe
            device.Lights[0].Diffuse = Color.White;
            device.Lights[0].Direction = new Vector3(1, 1, 1);
            device.Lights[0].Enabled = true;
            device.Lights[1].Type = LightType.Directional;
            device.Lights[1].Diffuse = Color.White;
            device.Lights[1].Direction = new Vector3(-1, -1, -1);
            device.Lights[1].Enabled = true;
            
            
        }

        public void InitializeMeshes()  // nowa metoda wczytująca obiekt
        {
            theXFile = new MyObject(ref device);
            //theXFile.LoadFromFile("c:\\x\\kostka.X");
            //theXFile.GenerateQube(2, 2, 2);

            theXFile.GenerateSphere(1, 20, 20);
        }

        public void InitializeDevice() // Druga nowość. Inicjujemy urządzenie.
        {
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            //presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            //presentParams.EnableAutoDepthStencil = true;
            device = new Device(0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, presentParams);
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) //Trzecia nowość. W zdarzeniu OnPaint będziemy rysować. 
        {
            InitializeCamera(); //inicjalizacji kamerki
            device.Clear(ClearFlags.Target, Color.White, 1.0f, 0);

              device.BeginScene(); // początek sceny 
              theXFile.Render();

              device.EndScene();  //koniec sceny

             t += 0.01f;
            device.Present(); 

            this.Invalidate(); //to po to aby windows przerysował formę
        }

        protected override void Dispose(bool disposing) // tu zwalniamy wszystkie komponenty
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }
        private void InitializeComponent() // tu metoda inicjująca komponenty
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true); //tu drugi dodatek, aby Windows dobrze przerysowywał okno.
 
            this.ClientSize = new System.Drawing.Size(500, 500);
            this.Name = "WinForm";
            this.Text = "DirectX in C#";
            

        }

        static void Main()  // tu uruchamiamy aplikację.
        {
            using (WinForm dx_form = new WinForm())
            {
                dx_form.InitializeDevice(); // Czwarta nowość. Trzeba zainicjować Device
                dx_form.InitializeMeshes();
                Application.Run(dx_form);
            }
        }

       
    }
}
 